mov eax,cs                                  ;把代码段基地址放入eax寄存器
mov ss,eax                                  ;把栈段基地址放入ss寄存器
mov sp,0x7c00                               ;把栈顶指针（0x7c00）放入sp寄存器

mov eax,[cs:pgdt+0x7c00+0x02]               ;把GDT的段基址放入eax寄存器
xor edx,edx                                 ;把edx寄存器清零
mov ebx,16                                  ;把16放入ebx寄存器
div ebx                                     ;eax=GDT的段基址/16,edx=GDT的段基址%16（余数）
                                            ;eax存放GDT的段基址
                                            ;edx存放GDT的偏移地址

mov ds,eax                                  ;把GDT的段基址放入ds寄存器
mov ebx,edx                                 ;把GDT的偏移地址放入ebx寄存器       

mov dword [ebx+0x00],0x00000000             ;GDT的第0项，空描述符
mov dword [ebx+0x04],0x00000000             ;GDT的第0项，空描述符

mov dword [ebx+0x08],0x0000ffff             ;GDT的第一项，界限低16位是ffff，基地址低16位是0000
mov dword [ebx+0x0c],0x00cf9200             ;GDT的第一项，界限高4位是0，属性是代码可读，粒度是4K

mov dword [ebx+0x10],0x7c0001ff             ;GDT的第二项，界限低16位是01ff，基地址低16位是7c00
mov dword [ebx+0x14],0x00409800             ;GDT的第二项，界限高4位是00，代码段描述符

mov dword [ebx+0x18],0x7c0001ff             ;GDT的第三项，基地址为0x00007c00，512字节
mov dword [ebx+0x1c],0x00409200             ;GDT的第三项，粒度位一个字节，数据段描述符

mov dword [ebx+0x20],0x7c00fffe             ;基地址为0x00007c00,界限为0xffffe
mov dword [ebx+0x24],0x00cf9600             ;粒度为4KB，向下扩展

mov word [cs:pgdt+0x7c00],39                ;初始化描述符表寄存器GDTR，39+1=40，一个段占8个字节，一共设置了5个段

lgdt [cs:pgdt+0x7c00]                       ;创建GDTR

in al,0x92
or al,0000_0010B
out 0x92,al

cli

mov eax,cr0
or eax,1
mov cr0,eax                                 ;开启保护模式

jmp 0x0010:dword flush                      ;指定第二个段，跳转到flush执行

[bits 32]
flush:
    mov eax,0x0018                          ;0x18=24，也就是GDT中的第三个段
    mov ds,eax                              ;把第三个段放入ds寄存器

    mov eax,0x0008                          ;0x08=8，也就是GDT中的第一个段（段地址为0000）       
    mov es,eax                              ;把段0000放入es寄存器
    mov fs,eax                              ;把段0000放入fs寄存器
    mov gs,eax                              ;把段0000放入gs寄存器

    mov eax,0x0020                          ;0x20=32，也就是GDT中的第四个段（段基址为7c00）
    mov ss,eax                              ;把段基址7c00传给ss段寄存器
    xor esp,esp                             ;清空esp寄存器

    mov dword [es:0x0b8000],0x072e0750      ;字符'P'、'.'及其显示属性
    mov dword [es:0x0b8004],0x072e074d      ;字符'M'、'.'及其显示属性
    mov dword [es:0x0b8008],0x07200720      ;两个空白字符及其显示属性
    mov dword [es:0x0b800c],0x076b076f      ;字符'o'、'k'及其显示属性

    mov ecx,pdgt-string-1                   ;冒泡排序，要排序的次数

@@1:
    push ecx
    xor bx,bx
@@2:
    mov ax,[string+bx]
    cmp ah,al
    jge @@3
    xchg al,ah
    mov [string+bx],ax
@@3:
    inc bx
    loop @@2
    pop ecx
    loop @@1 

    mov ecx,pdgt-string
    xor ebx,ebx

@@4:
    mov ah,0x07
    mov al,[string+ebx]
    mov [es:0xb80a0+ebx*2],ax
    inc ebx
    loop @@4

    hlt
;-------------------------------------------------------------------------------
     string           db 's0ke4or92xap3fv8giuzjcy5l1m7hd6bnqtw.'
;-------------------------------------------------------------------------------
     pgdt             dw 0
                      dd 0x00007e00      ;GDT的物理地址
;-------------------------------------------------------------------------------                             
     times 510-($-$$) db 0
                      db 0x55,0xaa